﻿namespace Microsoft.VisualStudio.TestTools.UnitTesting
{
    using System;
    using System.Diagnostics;
    using System.Diagnostics.Contracts;
    using System.Reflection;

    /// <summary>
    /// A utility class for working with exceptions.
    /// </summary>
    [DebuggerStepThrough]
    public static class ExceptionHelper
    {
        #region Public Methods
        /// <summary>
        /// Preserves the stack trace in the exception so that it is not lost when rethrown.
        /// </summary>
        /// <param name="exception">The exception.</param>
        public static void PreserveStackTrace(this Exception exception)
        {
            Contract.Requires(exception != null);

            MethodInfo preserveStackTrace = typeof(Exception).GetMethod(
                "InternalPreserveStackTrace",
                BindingFlags.Instance | BindingFlags.NonPublic);
            preserveStackTrace.Invoke(exception, null);
        }

        /// <summary>
        /// Rethrows the inner exception if a <see cref="TargetInvocationException"/> is thrown.
        /// </summary>
        /// <param name="action">The action that could raise a <see cref="TargetInvocationException"/>.</param>
        public static void RethrowInnerExceptionIfTargetInvocationException(Action action)
        {
            if (action == null)
            {
                throw new ArgumentNullException("action");
            }

            Contract.EndContractBlock();

            try
            {
                action();
            }
            catch (TargetInvocationException ex)
            {
                ex.InnerException.PreserveStackTrace();
                throw ex.InnerException;
            }
        }
        #endregion Public Methods
    }
}